package Methodes;


import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import DataIO.BinaireHydraulique;
import DataIO.BinaireThermique;
import DataIO.Data;


/**	
 * Résolution en variables binaires
 * @author poko
 *
 */
public class Prob_Cplex extends Methode {
	
	/** Variable pour le model cplex */
	IloCplex model;
	
	/** Vars pour le recours 0-> q+ 1-> q- */
	IloNumVar[][][] tabpaliers;
	
	/** Var demande */
	IloNumVar[][] vdemande;
	
	/** Var hydro */
	IloNumVar[][] vhydro;
	
	/** Instance de Data **/
	Data d;
	
	/** Données binaires thermiques */
	BinaireThermique bt;
	
	/** Données binaires hydraulique */
	BinaireHydraulique bh;
	
	/** Variable booléenne indiquant s'il faut lancer CPLEX en mode relaxation ou non. */ 
	boolean relaxed;
	
	/**
	 * Constrecteur.
	 * @param da
	 * @param bith
	 * @param bihy
	 * @param rlxd
	 * @throws IloException
	 */
	public Prob_Cplex(Data da, BinaireThermique bith, BinaireHydraulique bihy,boolean rlxd) throws IloException {
		model=new IloCplex();
		tabpaliers = new IloNumVar[4][][];
		tabpaliers[0] = new IloNumVar[3][7];
		tabpaliers[1] = new IloNumVar[4][7];
		tabpaliers[2] = new IloNumVar[4][7];
		tabpaliers[3] = new IloNumVar[5][7];
		relaxed=rlxd;
		vdemande=new IloNumVar[7][100];
		vhydro= new IloNumVar[7][8];
		d=da;
		bt=bith;
		bh=bihy;
		
	}
	/**
	 * Initialise le model et lance la résolution
	 * @throws IloException 
	 */
	public void solve() throws IloException{
		//fonction obj
		double cout[] =d.getCout();
		IloNumExpr obj = model.linearNumExpr() ;
		double[][] paliers = bt.getPaliers();
		double[][] trajectoiresh = bh.getTrajectoires();
		for(int k=0; k<7; k ++ ){
			int i = 0;
			for(double centrale[] : paliers){
				int j = 0;
				IloNumExpr unicite =  model.linearNumExpr();
				for(double palier : centrale){
					IloNumVar v;
					if(!relaxed){
						 v =  model.boolVar();
					}
					else {
						 v =  model.numVar(0, 1);
					}
					obj= model.sum(obj,model.prod(v,cout[i]*palier));
					tabpaliers[i][j][k]=v;
					unicite = model.sum(unicite,v);
					j++;
				}
				model.addEq(unicite, 1);
				i++;
			}
			//hydraulique
			IloNumExpr uniciteh =  model.linearNumExpr();
			
			int h=0;
			for(double traj : trajectoiresh[k]){
				
				IloNumVar v;
				if(!relaxed){
					 v =  model.boolVar();
				}
				else {
					 v =  model.numVar(0, 1);
				}
				vhydro[k][h]=v;
				obj= model.sum(obj,model.prod(v,cout[4]* traj));
				uniciteh = model.sum(uniciteh,v);
				h++;
			}
			model.addEq(uniciteh, 1);
		}
		//Ajout obj
		model.addMinimize(obj);
		//Contraintes
		double[][][][] scenarios = bt.getScenarios();
		double[][] sdemande = d.getDemande();
		//Variables d'activation, doit etre assez grande pour annuler la contrainte
		double m = 2000000;
		/**
		 * for days
			for scenarios
				for centrales
					for paliers
		 */
		for(int i = 0; i<7; i++){
			for(int j=0;j<100;j++){
				IloNumExpr contrainte = model.linearNumExpr();
				for(int k=0; k<4; k++){
					int p= 0;
					for(IloNumVar tp[]  : tabpaliers[k]){
						contrainte = model.sum(contrainte, model.prod(tp[i],scenarios[k][i][p][j]));
						p++;
					}
					//Ajout 0.65 var hydro ou pas -- bluff total
					for(int h=0;h<8;h++){
						contrainte=model.sum(contrainte,model.prod(vhydro[i][h],bh.getRho()));
					}
					
				}
				
				
				//ajout -(demande + M)variable à la contrainte
				IloNumVar yx = model.boolVar();
				vdemande[i][j]=yx;
				contrainte=model.diff(contrainte, model.prod(yx,(m + sdemande[i][j])  ));
				//Ajout greater than -M
				model.addGe(contrainte,-m);
			}
		}
		//niveaux de probabilité a chaque pas de temps pour l'équilibre * 100
		double p[] = {96,96,96,96,95,94,93};
		int ip=0;
		for(IloNumVar[] vd1 : vdemande){
			IloNumExpr contrainte = model.linearNumExpr();
			for(IloNumVar vd : vd1){
				contrainte =  model.sum(contrainte,vd);
			}
			model.addGe(contrainte, p[ip]);
			ip++;
		}
		
		
		
		model.exportModel("recuit.lp");
		double tempExecution=-model.getCplexTime();
		model.solve();
		tempExecution+=model.getCplexTime();
		model.writeSolution("sol_recuit");
		//model.getValues(null);
		
		System.out.println(model.getStatus());
		//System.out.println();
		
		double[][] meilleureSolution = new double[5][7];
		for(int i=0; i< 4; i++){
			int k =0;
			for(IloNumVar[] tab : tabpaliers[i]){
				for (int j=0; j< 7; j++){
					meilleureSolution[i][j]+=model.getValue(tab[j])*paliers[i][k];
				}	
				k++;
			}
			
		}
		int k =0;
		for(IloNumVar[] tab : vhydro){
			for (int j=0; j< 8; j++){
				meilleureSolution[4][k]+=model.getValue(tab[j])*trajectoiresh[k][j];
			}	
			k++;
		}
		//System.out.println(Arrays.deepToString(meilleureSolution));
		double[] maxCentrale1 = d.getMaxParCentrale().get(0);
		double[] maxCentrale2 = d.getMaxParCentrale().get(1);
		double[] maxCentrale3 = d.getMaxParCentrale().get(2);
		double[] maxCentrale4 = d.getMaxParCentrale().get(3);
		double[] maxCentrale5 = d.getMaxParCentrale().get(4);
		double coutMinimum=model.getObjValue();
		
		solution = new Solution(meilleureSolution, maxCentrale1, maxCentrale2, maxCentrale3, maxCentrale4, maxCentrale5, coutMinimum, tempExecution);
		System.out.println(model.getObjValue());
		System.out.println("Exeeeec :" + tempExecution);
		model.end();
	}

}
